package com.app.recordingscreen

import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.media.ImageReader
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.media.projection.MediaProjectionManager
import android.os.Bundle
import android.os.Environment
import android.util.DisplayMetrics
import android.util.Log
import android.view.Surface
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_z_recording_screen.*
import java.lang.Exception
import com.alibaba.android.arouter.facade.annotation.Route
import java.io.File
import java.io.FileOutputStream
import java.lang.ref.WeakReference
import java.util.concurrent.LinkedBlockingQueue
import kotlin.experimental.and


/**
 *  Create by zhouwd on 2020-02-21
 *  Describe:
 */
private const val REQUEST_CODE_A = 100

private val TAG = ZRecordingScreenActivity::class.java.simpleName

@Route(path = "/recording_screen_module/recording_screen_learn_activity")
class ZRecordingScreenActivity : AppCompatActivity() {

    init {
        try {
            System.loadLibrary("zrecordingscreen")
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private var mMediaProjectionManager: MediaProjectionManager? = null

    private var mImageReader: ImageReader? = null

    private var mediaCodec: MediaCodec ?= null

    private var surface: Surface ?= null
    private val TIMEOUT_USEC = 12000L
    @Volatile
    private var isRun = false

    private var configbyte: ByteArray? = null

    private var worker: Thread? = null
    private var pushFlowDataWorker: Thread? = null
    private var fos: FileOutputStream? = null

    private val linkedBlockingQueue = LinkedBlockingQueue<FlowDataPackage>(50)



    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_z_recording_screen)

        val filePath = Environment.getExternalStorageDirectory().toString() + File.separator  + "1234567.h264"

        val file = File(filePath)

        fos = FileOutputStream(file)


        initMPManager()

        initMediaCodec()

        //initImageReader()

        activity_z_recording_screen_start.setOnClickListener {
            mMediaProjectionManager?.let {
                val captureIntent = it.createScreenCaptureIntent()
                startActivityForResult(captureIntent, REQUEST_CODE_A)
            }

        }

        worker = Thread(Worker(this))
        pushFlowDataWorker = Thread(PushFlowDataWorker(this))

    }

    private fun initMediaCodec() {
        val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

        val displayMetrics = DisplayMetrics()
        windowManager.defaultDisplay.getMetrics(displayMetrics)
        Log.i(TAG, "onCreate --> initMediaCodec --> width --> ${displayMetrics.widthPixels} --> height --> ${displayMetrics.heightPixels}")
        val mediaFormat = MediaFormat.createVideoFormat("video/avc", 480, 852)
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
        //mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 3500000)
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25)
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)

        mediaCodec = MediaCodec.createEncoderByType("video/avc")
        mediaCodec?.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        surface = mediaCodec?.createInputSurface()
        mediaCodec?.start()
    }

    fun bytesToHexString(src: ByteArray?): String? {
        val stringBuilder = StringBuilder("")
        if (src == null || src.isEmpty()) {
            return null
        }
        for (i in src.indices) {
            val v = src[i] and 0xFF.toByte()
            val hv = Integer.toHexString(v.toInt())
            if (hv.length < 2) {
                stringBuilder.append(0)
            }
            stringBuilder.append(hv)
        }
        return stringBuilder.toString()
    }

    fun dequeueBuffer() {
        //Log.i(TAG, "dequeueBuffer---------> ")
        mediaCodec?.let {
            //Log.i(TAG, "dequeueBuffer---------> in")
            val mBufferInfo = MediaCodec.BufferInfo()
            var outputBufferIndex = it.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC)
            //Log.i(TAG, "dequeueBuffer---------> outputBufferIndex -> $outputBufferIndex")
            if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

            } else {
                while (outputBufferIndex >= 0) {
                    val outputBuffer = it.outputBuffers[outputBufferIndex]
                    val outData = ByteArray(mBufferInfo.size)
                    outputBuffer.get(outData)
                    Log.i(TAG, "dequeueBuffer---------> mBufferInfo.size --> ${mBufferInfo.size}")

                    try {
                        fos?.write(outData)
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                    //Log.e("zzz", "pps: " + ByteUtil.bytesToHexSpaceString(pps))
                    val flowDataPackage = FlowDataPackage()
                    val spsb = it.outputFormat.getByteBuffer("csd-0")
                    val sps = ByteArray(spsb.remaining())
                    spsb.get(sps, 0, sps.size)
                    flowDataPackage.spsData = sps
                    flowDataPackage.spsSize = sps.size
                    Log.e(TAG, "sps: --> ${bytesToHexString(sps)}")

                    val ppsb = it.outputFormat.getByteBuffer("csd-1")
                    val pps = ByteArray(ppsb.remaining())
                    ppsb.get(pps, 0, pps.size)
                    flowDataPackage.ppsData = pps
                    flowDataPackage.ppsSize = pps.size


                    Log.e(TAG, "pps: --> ${bytesToHexString(pps)}")
                    //pushSPSPPS(sps, sps.size, pps, pps.size)
                    flowDataPackage.videoData = outData
                    flowDataPackage.videoDataSize = mBufferInfo.size
                    flowDataPackage.isKeyFrame = mBufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME
                    //pushFlowData(outData, mBufferInfo.size, mBufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME)

                    linkedBlockingQueue.offer(flowDataPackage)

                    it.releaseOutputBuffer(outputBufferIndex, false)
                    outputBufferIndex = it.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC)
                }
            }
        }
    }

    fun getFlowDataPackage() : FlowDataPackage = linkedBlockingQueue.take()



    class PushFlowDataWorker(zRecordingScreenActivity: ZRecordingScreenActivity) : Runnable {
        private var mActivity: ZRecordingScreenActivity? = null

        init {
            val weekReference = WeakReference<ZRecordingScreenActivity>(zRecordingScreenActivity)
            mActivity = weekReference.get()
        }
        override fun run() {
            mActivity?.let {
                while (it.isRun) {

                    val flowDataPackage = it.getFlowDataPackage()

                    try {
                        Log.e(TAG, "PushFlowDataWorker: --> $flowDataPackage")
                        Log.e(TAG, "PushFlowDataWorker: --> spsSize --> ${flowDataPackage.spsSize} --> ppsSize --> ${flowDataPackage.ppsSize} --> ${flowDataPackage.videoDataSize}")
                        it.pushSPSPPS(flowDataPackage.spsData!!, flowDataPackage.spsSize!!, flowDataPackage.ppsData!!, flowDataPackage.ppsSize!!)

                        it.pushFlowData(flowDataPackage.videoData!!, flowDataPackage.videoDataSize!!, flowDataPackage.isKeyFrame!!)
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }

                }
            }


        }
    }

    class Worker(zRecordingScreenActivity: ZRecordingScreenActivity) : Runnable {

        private var mActivity: ZRecordingScreenActivity? = null

        init {
            val weekReference = WeakReference<ZRecordingScreenActivity>(zRecordingScreenActivity)
            mActivity = weekReference.get()
        }

        override fun run() {

            mActivity?.let {
                while (it.isRun) {

                    mActivity?.dequeueBuffer()

                }
            }


        }
    }

    /**
     * 初始化图片
     */
    private fun initImageReader() {
        val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

        val displayMetrics = DisplayMetrics()
        windowManager.defaultDisplay.getMetrics(displayMetrics)

        mImageReader = ImageReader.newInstance(displayMetrics.widthPixels, displayMetrics.heightPixels, PixelFormat.RGBA_8888, 2)

        mImageReader?.setOnImageAvailableListener(mImageAvailableListener, null)

    }

    private val mImageAvailableListener = ImageReader.OnImageAvailableListener {
        Log.i(TAG, "mImageAvailableListener")
        val image = it.acquireNextImage()
        try {
            Log.i(TAG, "mImageAvailableListener width -> ${image.width} ::: height -> ${image.height}")

            Log.i(TAG, "mImageAvailableListener planes -> ${image.planes.size}")
            val plane = image.planes[0]
            val byteBuffer = plane.buffer

            val rowPadding = plane.rowStride - plane.pixelStride * image.width

            Log.i(TAG, "mImageAvailableListener pixelStride -> ${plane.pixelStride} :: rowStride -> ${plane.rowStride} :: rowPadding -> $rowPadding")
            //val data = ByteArray()

            //byteBuffer.get(data)

        } catch (e: Exception) {

            e.printStackTrace()

        } finally {
            image.close()
        }



    }

    private fun initMPManager() {

        mMediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager?



    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        try {
            if (requestCode == REQUEST_CODE_A) {
                val mediaProjection = mMediaProjectionManager?.getMediaProjection(resultCode, data!!)

                val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

                val displayMetrics = DisplayMetrics()
                windowManager.defaultDisplay.getMetrics(displayMetrics)

                mediaProjection?.createVirtualDisplay("screen-mirror", 480, 852, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                        surface, null, null)
                isRun = true

                val initCode = pushFlowInit("rtmp://148.70.31.164/live/test")
                Log.i(TAG, "onCreate --> initCode --> $initCode")

                worker?.start()
                pushFlowDataWorker?.start()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }


    private external fun pushFlowInit(url: String):Int

    private external fun pushSPSPPS(spsData: ByteArray, spsSize: Int, ppsData: ByteArray, ppsSize: Int)

    private external fun pushFlowData(data: ByteArray, size: Int, iskeyFrame: Boolean): Int

    private external fun pushFlowDestory(): Int

}

class FlowDataPackage {

    var spsData: ByteArray? = null

    var spsSize: Int? = null

    var ppsData: ByteArray? =null

    var ppsSize: Int? = null

    var videoData: ByteArray? = null

    var videoDataSize: Int? = null

    var isKeyFrame: Boolean? = null

}
